package hn.sigit.logic.security;

import hn.sigit.logic.ladmshadow.Util;

import hn.sigit.git.model.Cities;
import hn.sigit.git.model.Countries;
import hn.sigit.logic.bpm.TransactionController;
import hn.sigit.model.hnd.administrative.HND_NaturalPersonExtParty;
import hn.sigit.model.hnd.administrative.dao.HND_NaturalPersonExtPartyDAO;
import hn.sigit.model.ladm.administrative.LA_BAUnit;
import hn.sigit.model.ladm.administrative.LA_RightType;
import hn.sigit.model.ladm.external.ExtParty;
import hn.sigit.model.ladm.external.dao.ExtPartyDAO;
import hn.sigit.model.ladm.party.LA_Party;
import hn.sigit.model.ladm.party.dao.LA_PartyDAO;
import hn.sigit.model.ladm.spatialunit.LA_VolumeType;
import hn.sigit.model.ladm.spatialunit.LA_VolumeValue;
import hn.sigit.model.ladm.special.Rational;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.util.Stack;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.servlet.http.HttpServletResponse;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernatespatial.criterion.SpatialRestrictions;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.remoting.WebRemote;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

import org.geotools.feature.FeatureCollections;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.Transaction;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.io.WKTWriter;

@Name("testDao")
@Scope(value = ScopeType.CONVERSATION)
@AutoCreate
public class TestDAO {
	@In
	private Session commonsDatabase;
	
	@In
	private TransactionController transactionController;
	
	private List<Countries> countries;

	@WebRemote
	public String webRemote(double x, double y) {
		// TODO Auto-generated method stub
		System.out.println(String.format("Las coords son (%f, %f)", x, y));
		return String.valueOf(x + y);
	}

	private static SimpleFeatureType createFeatureType() {
		SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
		builder.setName("Location");
		builder.setCRS(DefaultGeographicCRS.WGS84); // <- Coordinate reference
													// system

		// add attributes in order
		builder.add("gid", Integer.class);
		builder.length(15).add("countryName", String.class); // <- 15 chars
																// width for
																// name field
		builder.add("theGeom", MultiPolygon.class);

		// build the type
		final SimpleFeatureType COUNTRY = builder.buildFeatureType();

		return COUNTRY;
	}
	
	private void printCity(Cities city) {
		System.out.println(String.format("City: %s, num: %d, den: %d, value: %f, SIZE: %f, VOLUME TYPE: %s",
				city.getCityName(),
				city.getShare().getNumerator(),
				city.getShare().getDenominator(),
				city.getShare().getValue(),
				city.getVolumeValue().getVolumeSize(),
				city.getVolumeValue().getVolumeType().toString()));
	}
	
	public void testSavingMultiple() {
		HND_NaturalPersonExtParty natPersExtParty = new HND_NaturalPersonExtParty();
		System.out.println("ID: " + natPersExtParty.getExtPID());
		
		natPersExtParty.setFirstName1("Pancracio");
		natPersExtParty.setLastName1("LOOP");
		natPersExtParty.setGender('M');
		natPersExtParty.setMaritalStatus('S');
		
		ExtPartyDAO.save(natPersExtParty);
		System.out.println("Despues de 1ra salvada. ID: " + natPersExtParty.getExtPID());

		natPersExtParty = natPersExtParty.clone();
		natPersExtParty.setExtPID(0);
		natPersExtParty.setLastName1("LOOP_ZERO");
		
		ExtPartyDAO.save(natPersExtParty);
		System.out.println("Despues de 2da SALVADA. ID: " + natPersExtParty.getExtPID());

	}
	
	public void testCopyShadow() {
		//Util.copyPropertyData(2, 388, false);
		
	}
	
	public void testQuery() {
		List<LA_Party> list;
		list = HND_NaturalPersonExtPartyDAO.loadParties();
		for (LA_Party lap : list)
			System.out.println(lap.getORMID());
	}
	
	public void testRRR() {
		String hql = "from Cities";
		@SuppressWarnings("unchecked")
		List<Cities> baUnits = commonsDatabase.createQuery(hql).list();
		
		for (Cities city : baUnits) {
			printCity(city);
		}
		
		Cities newCity = new Cities();
		newCity.setCityName("Tela");
		newCity.setShare(new Rational(4, 9));
		newCity.setType(LA_RightType.FISHING);
		newCity.setVolumeValue(new LA_VolumeValue(LA_VolumeType.CALCULATED_VOLUME, new BigDecimal(223.141592654)));
		
		commonsDatabase.saveOrUpdate(newCity);
		
		hql = "from Cities c WHERE c.type = hn.sigit.model.ladm.administrative.LA_RightType.FISHING";
		baUnits = commonsDatabase.createQuery(hql).list();
		for (Cities city : baUnits) {
			printCity(city);
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<Countries> getCountries() {
		if (null == countries) {
			String hql = "from Countries";
			countries = (ArrayList<Countries>) commonsDatabase.createQuery(hql).list();
		}

		return countries;
	}
	

	@SuppressWarnings("unchecked")
	public List<Countries> findTouches(int gid) {
		ArrayList<Countries> result;
		MultiPolygon filter;
		String hql = "from Countries where gid = " + gid;
		Countries ct = (Countries) commonsDatabase.createQuery(hql)
				.uniqueResult();
		filter = (MultiPolygon) ct.getTheGeom();

		Criteria resultCriteria = commonsDatabase
				.createCriteria(Countries.class);

		resultCriteria.add(SpatialRestrictions.touches("theGeom", filter));
		result = (ArrayList<Countries>) resultCriteria.list();

		result.add(ct);

		return result;
	}
	
	public void printCountryWKT(int gid) {
		MultiPolygon filter;
		String hql = "from Countries where gid = " + gid;
		Countries ct = (Countries) commonsDatabase.createQuery(hql)
				.uniqueResult();
		filter = (MultiPolygon) ct.getTheGeom();
		
		WKTWriter wktWriter = new WKTWriter();
		System.out.println(wktWriter.write(filter.getEnvelope()));
	}
	
	private void writeZipFile(final File[] inputFiles, OutputStream outStream) {
		final int BUFFER = 4192;

		try {
			ZipOutputStream out = new ZipOutputStream(outStream);
			BufferedInputStream origin = null;
			byte data[] = new byte[BUFFER];
			for (File file : inputFiles) {
				if (file.getName().endsWith(".zip")) continue;
				
				try {
					FileInputStream fi = new FileInputStream(file);
					origin = new BufferedInputStream(fi, BUFFER);
					ZipEntry entry = new ZipEntry(file.getName());
					out.putNextEntry(entry);

					int count;
					while ((count = origin.read(data, 0, BUFFER)) != -1)
						out.write(data, 0, count);
					origin.close();
					System.out.println("Se zipeo " + file.getName());
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private File[] generateShapeFiles(List<Countries> geomList) {
		File[] resultFiles = null;

		SimpleFeatureType TYPE = createFeatureType();
		SimpleFeatureCollection collection = FeatureCollections.newCollection();
		SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);

		List<Countries> listTouches = geomList;
		
		
		Iterator<Countries> it = listTouches.iterator();
		Countries ct;
		while (it.hasNext()) {
			ct = it.next();
			featureBuilder.add(ct.getGid());
			featureBuilder.add(ct.getCountryName());
			featureBuilder.add(ct.getTheGeom());
			SimpleFeature feature = featureBuilder.buildFeature(null);
			collection.add(feature);
		}

		final File newFile = new File("/Users/rodyce/out.shp");

		ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();

		Map<String, Serializable> params = new HashMap<String, Serializable>();
		try {
			params.put("url", newFile.toURI().toURL());
			params.put("create spatial index", Boolean.FALSE);
		} catch (MalformedURLException ex) {
			System.err.println("URL mal formado: " + ex.getMessage());
		}

		try {
			ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory
					.createNewDataStore(params);
			newDataStore.createSchema(TYPE);

			Transaction transaction = new DefaultTransaction("create");

			String typeName = newDataStore.getTypeNames()[0];
			SimpleFeatureSource featureSource = newDataStore
					.getFeatureSource(typeName);

			if (featureSource instanceof SimpleFeatureStore) {
				SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;

				featureStore.setTransaction(transaction);
				try {
					featureStore.addFeatures(collection);
					transaction.commit();
					
					File dir = new File(newFile.getParent());
					resultFiles = dir.listFiles(new FilenameFilter() {
						@Override
						public boolean accept(File dir, String name) {
							String shpName = newFile.getName();
							return name.startsWith(shpName.substring(0, shpName
									.length() - 4));
						}
						
					});

				} catch (Exception problem) {
					problem.printStackTrace();
					transaction.rollback();

				} finally {
					transaction.close();
				}

			} else {
				System.out.println(typeName
						+ " does not support read/write access");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return resultFiles;
	}
	
	public void download() throws IOException {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        File[] shapeFiles = null;
        try {
        	//Paso 1: Obtener lista de entidades que satisfagan el query espacial
        	String gid = transactionController.getGid();
        	List<Countries> geomList = findTouches(Integer.valueOf(gid).intValue());
        	
        	//Paso 2: Generar archivos shape de la geometria
        	shapeFiles = generateShapeFiles(geomList);
        	
        	//Paso 3: Escribir al stream de salida ZIP los archivos shape generados
			ExternalContext externalContext = facesContext.getExternalContext();
			HttpServletResponse response = (HttpServletResponse) externalContext
					.getResponse();

			response.reset(); // Some JSF component library or some Filter might
								// have set some headers in the buffer
								// beforehand. We want to get rid of them, else
								// it may collide.
			response.setContentType("application/x-download");
			response.setHeader("Content-disposition",
					"attachment; filename=\"shape.zip\"");

			BufferedOutputStream output = null;

			try {
				output = new BufferedOutputStream(response.getOutputStream());

				writeZipFile(shapeFiles, output);
			} finally {
				if (null != output)
					output.close();
			}

        }
        catch (Exception e) {
        	e.printStackTrace();
        }
        finally {
        	//marcar los archivos shape generados para ser eliminados
        	//al terminar la instancia de la Java VM
        	for (File f : shapeFiles)
        		f.deleteOnExit();
        	
			facesContext.responseComplete();
        }
    }
	
	public List<Countries> suggest(Object value) {
		String input = (String) value;
		ArrayList<Countries> result = new ArrayList<Countries>();
		for (Countries ct : getCountries())
			if (ct.getCountryName().toLowerCase().startsWith(input.toLowerCase()))
				result.add(ct);
		
		return result;
	}
	
	public void dummy() {
		
	}
}
